package com.service.impl;

import com.api.repository.AddressRepository;
import com.api.repository.OrderItemRepository;
import com.api.repository.OrderRepository;
import com.api.service.ExampleService;
import com.domain.Address;
import com.domain.Order;
import com.domain.OrderItem;
import com.repository.AddressRepositoryImpl;
import com.repository.OrderItemRepositoryImpl;
import com.repository.OrderRepositoryImpl;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@Data
public class OrderServiceImpl implements ExampleService, InitializingBean {

    private final OrderRepository orderRepository;

    private final OrderItemRepository orderItemRepository;

    private final AddressRepository addressRepository;

    @Autowired
    public OrderServiceImpl( final DataSource dataSource, final AbstractPlatformTransactionManager transactionManager) {
        String useRangeFlag = System.getProperty("useRangeFlag", "false");

        if (useRangeFlag.equals("false")) {
            orderRepository = new OrderRepositoryImpl(dataSource, transactionManager);
            orderItemRepository = new OrderItemRepositoryImpl(dataSource, transactionManager);
            addressRepository = new AddressRepositoryImpl(dataSource);
        } else {
            orderRepository = new RangeOrderRepositoryImpl(dataSource);
            orderItemRepository = new RangeOrderItemRepositoryImpl(dataSource);
            addressRepository = new AddressRepositoryImpl(dataSource);
        }
    }


    public OrderServiceImpl(final OrderRepository orderRepository, final OrderItemRepository orderItemRepository, final AddressRepository addressRepository) {
        this.orderRepository = orderRepository;
        this.orderItemRepository = orderItemRepository;
        this.addressRepository = addressRepository;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initEnvironment();
        ;
    }

    public void initEnvironment() throws SQLException {
        orderRepository.createTableIfNotExists();
        orderItemRepository.createTableIfNotExists();
        orderRepository.truncateTable();
        orderItemRepository.truncateTable();
        initAddressTable();
    }

    private void initAddressTable() throws SQLException {
        addressRepository.createTableIfNotExists();
        addressRepository.truncateTable();
        initAddressData();
    }

    private void initAddressData() throws SQLException {
        for (int i = 0; i < 10; i++) {
            insertAddress(i);
        }
    }

    private void insertAddress(final int i) throws SQLException {
        Address address = new Address();
        address.setAddressId((long) i);
        address.setAddressName("address_" + i);
        addressRepository.insert(address);
    }

    public void cleanEnvironment() throws SQLException {
        orderRepository.dropTable();
        orderItemRepository.dropTable();
        addressRepository.dropTable();
    }

    public void joinOperate() throws SQLException {
//        System.out.println("-------------- Process Success Begin ---------------");
//        List<Long> orderIds = insertData();

        log.info("-------------- joinOperate print data Begin ---------------");
        List<Order> orders = orderRepository.selectJoin();
        for (Order each : orders) {
            System.out.println(each);
        }
        log.info("-------------- joinOperate print data end ---------------");
//        deleteData(orderIds);
//        System.out.println("-------------- Process Success End ---------------");
    }

    @Transactional(rollbackFor = {RuntimeException.class})
    public void processSuccess() throws SQLException {
        System.out.println("-------------- Process Success Begin ---------------");
        List<Long> orderIds = insertData();
        printData();
        deleteData(orderIds);
        printData();
        System.out.println("-------------- Process Success Finish --------------");

    }

    public void processFailure() throws SQLException {
        System.out.println("-------------- Process Failure Begin ---------------");
        insertData();
        System.out.println("-------------- Process Failure Finish --------------");
        throw new RuntimeException("Exception occur for transaction test.");
    }

    private List<Long> insertData() throws SQLException {
        System.out.println("---------------------------- Insert Data ----------------------------");
        List<Long> result = new ArrayList<>(10);
        for (int i = 1; i <= 10; i++) {
            Order order = insertOrder(i);
            insertOrderItem(i, order);
            result.add(order.getOrderId());
        }
        return result;
    }

    private Order insertOrder(final int i) throws SQLException {
        Order order = new Order();
        order.setUserId(i);
        order.setAddressId(i);
        order.setStatus("INSERT_TEST");
        orderRepository.insert(order);
        return order;
    }

    private void insertOrderItem(final int i, final Order order) throws SQLException {
        OrderItem item = new OrderItem();
        item.setOrderId(order.getOrderId());
        item.setUserId(i);
        item.setStatus("INSERT_TEST");
        orderItemRepository.insert(item);
    }

    private void deleteData(final List<Long> orderIds) throws SQLException {
        System.out.println("---------------------------- Delete Data ----------------------------");
        for (Long each : orderIds) {
            orderRepository.delete(each);
            orderItemRepository.delete(each);
        }
    }

    public void printData() throws SQLException {
        System.out.println("---------------------------- Print Order Data -----------------------");
        for (Object each : orderRepository.selectAll()) {
            System.out.println(each);
        }
        System.out.println("---------------------------- Print OrderItem Data -------------------");
        for (Object each : orderItemRepository.selectAll()) {
            System.out.println(each);
        }
    }


}
